package bancosys.tec.utils.db;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.regex.Pattern;

/**
 * @author Marcio Ribeiro (mmr)
 * @created Apr 26, 2007
 */
public abstract class StatementSplitter {

    private static final Pattern NULL_PATTERN = Pattern.compile("/\\*[^*]+\\*/");

    private static final Pattern COMMENT_PATTERN = Pattern.compile("--(?!NoUpperCase)(.*)");

    private static final Pattern SPACE_PATTERN = Pattern.compile("[\t ]+");

    private static final Pattern NL1_PATTERN = Pattern.compile("(?:\r?\n)+");

    private static final Pattern NL2_PATTERN = Pattern.compile("^\r?\n");

    private static final Pattern EOLSPACE_PATTERN = Pattern.compile("[\t ]*$");

    private final Pattern delimiterPattern;

    protected URL file;

    protected char delimiterChar;

    protected int lineNumber;

    /**
     * C'tor
     * 
     * @param file the file URL
     * @param delimiterChar the delimiter char
     */
    public StatementSplitter(URL file, char delimiterChar) {
        this.lineNumber = 1;
        this.file = file;
        this.delimiterChar = delimiterChar;
        this.delimiterPattern = Pattern.compile(".*" + this.delimiterChar + "[\t ]*$");
    }

    /**
     * Splits statements using the delimiter char defined in this instance and execute each statement.
     * 
     * @throws IOException if the input file cannot be read
     * @throws CouldNotProcessStatementException if a statement fails to execute
     */
    public void split() throws IOException, CouldNotProcessStatementException {
        BufferedReader in =
                new BufferedReader(new InputStreamReader(this.file.openStream(), Charset.forName("UTF-8")));
        try {
            String line = null;
            StringBuilder st = new StringBuilder();
            while ((line = in.readLine()) != null) {
                line = this.cleanUpStatement(line);
                if (this.isEndOfStatement(line)) {
                    st.append(line.substring(0, line.indexOf(this.delimiterChar)));
                } else {
                    this.lineNumber++;
                    st.append(line + "\n");
                    continue;
                }
                this.processStatement(st.toString());
                this.lineNumber++;
                st = new StringBuilder();
            }
        } finally {
            in.close();
        }
    }

    /**
     * if the statement contains the delimiter char
     * 
     * @param line the line begin read
     * @return true if this line ends with the delimiter char
     */
    protected boolean isEndOfStatement(String line) {
        return this.delimiterPattern.matcher(line).matches();
    }

    /**
     * @param s a line
     * @return a cleaned up line according to the paterns defined in this class
     */
    protected String cleanUpStatement(String s) {
        String tmp = NULL_PATTERN.matcher(s).replaceAll("");
        tmp = COMMENT_PATTERN.matcher(tmp).replaceAll("");
        tmp = SPACE_PATTERN.matcher(tmp).replaceAll(" ");
        tmp = NL1_PATTERN.matcher(tmp).replaceAll("\n");
        tmp = NL2_PATTERN.matcher(tmp).replaceAll("");
        tmp = EOLSPACE_PATTERN.matcher(tmp).replaceAll("");
        return tmp;
    }

    /**
     * Asks the implementation to process this split statement
     * 
     * @param st a split statement
     * @throws CouldNotProcessStatementException if the statement cold not be processed
     */
    protected abstract void processStatement(String st) throws CouldNotProcessStatementException;
}